This document aims to show how to use Diana to create plots from Python scripts and from within the ipython notebook.
To get access to the Python modules that expose Diana's plotting features, you need to install the python-diana
package from the local package repository. Either use the package manager to do this, or enter the following in a
terminal:
apt-get install python-diana
Some examples can also be found in the python-diana-examples
package.
You can use Diana's functionality from Python by importing the bdiana
and plotcommands
modules from the metno
package. If you are using
an old version of ipython (earlier than version 1.0) then it is also useful to import the embed
function from the
metno.ipython_extensions
module.
In [1]:
# Import the modules needed to use Diana.
from metno import bdiana, plotcommands
# Import an extension that lets us embed an image into a worksheet.
from metno.ipython_extensions import embed
In [2]:
# Create a BDiana object - this provides a simpler interface than calling Diana functions directly.
b = bdiana.BDiana()
By default, Diana doesn't know anything about data files or other resources. The locations of data files,
definitions of plot types, palettes, and other pieces of information are stored in one or more setup files.
We choose one of these and use the setup
method to let Diana know where to get data from.
In [3]:
# Load a setup file - this describes where Diana finds data files and other resources.
b.setup("/etc/diana/setup/diana.setup-COMMON")
The method will return True
if the setup file was loaded correctly.
In [4]:
models = b.getModels()
# Uncomment the following lines to see the list of models known to Diana.
models = list(models)
models.sort()
models
We will use the WAM 4km model.
In [5]:
model = "WAM.4km"
To find the fields available for this model, we call the getFields
method with the model.
In [6]:
fields = b.getFields(model)
# Uncomment the following lines to see the list of fields available.
fields = list(fields)
fields.sort()
fields
We will use the mean wave direction field.
In [7]:
field = "mean_wave_direction"
Before we can plot anything, we need to know which times we have field data for. To do this, we call the
getFieldTimes
method with the model and field as arguments.
In [8]:
times = b.getFieldTimes(model, field)
# Show the last five times.
times[-5:]
Having obtained the model, field and time information, we can now create a plot. To do this, we need to create some plot commands that describe how the plot will appear.
Diana creates plots by reading and executing a series of plot commands. To create a plot, we need to create these
commands. First, we write a plot command to create a map by creating a Map
object and setting some options. These
are explained below.
In [9]:
m = plotcommands.Map()
m.setOption("map", "Gshhs-Auto")
m.setOption("backcolour", "white")
m.setOption("land", "on")
m.setOption("land.colour", "flesh")
map
option describes the map data to use. "Gshhs-Auto" is a good general map to use.backcolour
option describes the colour to use for the plot background. Named colours or colour
components can be used to specify the colour. Use "0:0:0:0" for a transparent background.land
option determines whether the land is filled.land.colour
describes the fill colour used.We write a plot command for the field by creating a Field
object and setting the model
, plot
and plottype
options. In the example below, we pass these to the class initialisation method instead of setting them separately.
Either way can be used.
In [10]:
f = plotcommands.Field(model = model, plot = field, plottype = "direction")
#f.setOption("model", model)
#f.setOption("plot", field)
#f.setOption("plottype", "direction")
Normally, plot commands are contained in an input file that describes what kind of output to produce, as well as
other syntax to ensure that Diana understands exactly what is to be plotted. For convenience, the BDiana
class
provides the setPlotCommands
method that lets us ignore those details. We pass the plot commands to
this method to set up the plot.
In [11]:
b.setPlotCommands([m, f])
# Print the actual plot commands for clarity.
print m.text()
print f.text()
The last thing we need to do before asking Diana to plot the figure is to set the plot time. We choose the last one available from the collection we obtained earlier.
In [12]:
b.setPlotTime(times[-1])
A return value of True
indicates that the specified time was acceptable. If False
was returned then the time is
not available. This shouldn't happen in this example.
Now we are able to create an image for the plot we specified. This one will be 500 pixels wide and 500 pixels high.
In [13]:
im = b.plotImage(500, 500)
When using an ipython notebook, it is useful to be able to preview the image we obtained. The embed
function does
this for us.
In [14]:
embed(im)
In [15]:
a = plotcommands.Area(name = "Norge")
Now we can create the plot again using this area.
In [16]:
b.setPlotCommands([a, m, f])
b.setPlotTime(times[-1])
im = b.plotImage(500, 500)
embed(im)
In [17]:
field2 = "maximum_wave_height"
times2 = b.getFieldTimes(model, field2)
if times[-1] in times2:
print times[-1], "is available in both fields."
else:
print times[-1], "is not available for", field2
If the time is available in both fields then we can create a plot command and include it in the list we pass to
the setPlotCommands
method. In this case, we will include it before the command to show the mean wave direction.
In [18]:
f2 = plotcommands.Field(model = model, plot = field2, plottype = "contour")
f2.setOption("palettecolours", "standard")
f2.setOption("line.interval", 1)
b.setPlotCommands([a, m, f2, f])
b.setPlotTime(times[-1])
im = b.plotImage(500, 500)
embed(im)
In [19]:
embed(im.copy(200, 300, 50, 50).scaled(200, 200))
If we set the antialias
plot option in the first field, the arrows appear smoother
but we don't affect the way the rest of the plot is displayed.
In [20]:
f.setOption("antialiasing", True)
b.setPlotCommands([a, m, f2, f])
b.setPlotTime(times[-1])
im = b.plotImage(500, 500)
embed(im.copy(200, 300, 50, 50).scaled(200, 200))
Note that the frame around the field area is also anti-aliased. The frame itself can be disabled in plots. Note that there are two frames being plotted here, so we need to disable them both.
In [21]:
f.setOption("frame", 0)
f2.setOption("frame", 0)
b.setPlotCommands([a, m, f2, f])
b.setPlotTime(times[-1])
im = b.plotImage(500, 500)
embed(im.copy(200, 300, 50, 50).scaled(200, 200))
In [22]:
b.plotPDF(500, 500, "/tmp/plot.pdf")
You can discard the return value from this call if you want. The output file can be found in the location passed
to the plotPDF
method call.
In [23]:
l1 = plotcommands.Label()
l1.setOption("data")
b.setPlotCommands([a, m, f2, f, l1])
b.setPlotTime(times[-1])
im = b.plotImage(500, 500)
embed(im)
The Label
plot command accept a number of different options. We can see some of these by calling its getOptions
method.
In [24]:
l1.getOptions()
Let's try moving the label to the top-left of the plot and changing the font size.
In [25]:
l1.setOption("valign", "top")
l1.setOption("fontsize", 8)
b.setPlotCommands([a, m, f2, f, l1])
b.setPlotTime(times[-1])
im = b.plotImage(500, 500)
embed(im)
We can also show the associated table for the maximum wave height plot, but we need to ensure that this plot
generates the information needed for this by setting its table
option to 1 and setting the
line.values
option.
In [26]:
f2 = plotcommands.Field(model = model, plot = field2, plottype = "fill_cell")
f2.setOption("frame", 0)
f2.setOption("antialiasing", True)
f2.setOption("palettecolours", "standard")
f2.setOption("table", 1)
f2.setOption("line.values", [0,1,2,3,4,5,6,7,8,9,10])
f2.text()
The command to show the table is a label that uses the anno
option with a value which contains some special
syntax. We place the label in the bottom-left corner of the plot. It is necessary to set the polystyle
option to
either none
or border
.
In [27]:
l2 = plotcommands.Label()
l2.setOption("anno", "<table,fcolour=white>")
l2.setOption("halign", "left")
l2.setOption("valign", "bottom")
l2.setOption("polystyle", "none")
l2.setOption("fontsize", 8)
b.setPlotCommands([a, m, f2, f, l1, l2])
b.setPlotTime(times[-1])
im = b.plotImage(500, 500)
embed(im)
In [28]:
import pyproj
A projection object is created by defining which projection and datum to use. We use the projection object to obtain a rectangle in the projected coordinate system whose bottom-left and top-right corners are specified as latitude and longitude values.
In [29]:
p = pyproj.Proj(proj="utm", datum="WGS84")
x1, y1 = p(-15, 25)
x2, y2 = p(35, 75)
With the projection and rectangle defined, we can create a plot command
In [30]:
a2 = plotcommands.Area(proj4string = p.srs, rectangle = (x1, x2, y1, y2))
b.setPlotCommands([a2, m, f2, f, l1, l2])
b.setPlotTime(times[-1])
im = b.plotImage(500, 500)
embed(im)
Usually, all the fields you want to use are listed in the setup file that is passed to
the BDiana
object's setup
method. However, you may want to load another field that is
not listed there. To achieve this, the BDiana
object provides an addField
method that
performs the work of loading the field into Diana.
For example, we can obtain a data file from a THREDDS server and examine its contents. We must provide a model name so that we can reference the data.
In [31]:
url = "http://thredds.met.no/thredds/dodsC/myocean/siw-tac/siw-fmi-baltic/2013/03/ice_thic_baltic_201303311400.nc"
b.addModel("baltic_sea_ice_thickness", url)
The new model can now be found in the list of available models.
In [32]:
models = list(b.getModels())
"baltic_sea_ice_thickness" in models
The fields provided by the model can be found as usual.
In [33]:
fields = list(b.getFields("baltic_sea_ice_thickness"))
fields.sort()
fields
We plot one of these fields in the usual way.
In [34]:
a = plotcommands.Area(name = "Europa")
m = plotcommands.Map(map = "Gshhs-Auto", backcolour = "white", land = "on")
m.setOption("land.colour", "flesh")
f = plotcommands.Field(model = "baltic_sea_ice_thickness",
plot = "ice_thickness", plottype = "fill_cell")
times = b.getFieldTimes("baltic_sea_ice_thickness", "ice_thickness")
b.setPlotCommands([a, m, f])
b.setPlotTime(times[-1])
embed(b.plotImage(500, 500))
Sometimes, we only want to use the data from a particular file, and so we want to remove
all other information about other models when we load it. To do this, we call the
addField
with the clear
argument set to True
.
In [35]:
b.addModel("baltic_sea_ice_thickness", url, clear = True)
Now, the list of available models only contains the model we loaded.
In [36]:
b.getModels()